package com.example.interesting.distributed.lock.realize;

import com.example.interesting.distributed.lock.realize.entity.DistributedLockInfo;
import com.example.interesting.distributed.lock.realize.properties.DistributedLockProperties;
import com.example.interesting.distributed.lock.realize.strategy.DefaultDistributedLockExecutorStrategy;
import com.example.interesting.distributed.lock.realize.strategy.DistributedLockExecutorStrategy;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author fyw
 * @version 1.0
 * @description: 分布式锁手动使用模板类
 * @date 2024/3/16 13:48
 */
@RequiredArgsConstructor
public class DistributedLockTemplate implements InitializingBean {

    /**
     * 配置信息
     */
    private final DistributedLockProperties distributedLockProperties;

    /**
     * 所有的执行器
     * 目前仅redisson执行器；可扩展
     */
    private final List<DistributedLockExecutorStrategy> executors;

    /**
     * 执行器集合
     */
    private final Map<Class<? extends DistributedLockExecutorStrategy>, DistributedLockExecutorStrategy> executorMap = new LinkedHashMap<>();

    /**
     * 默认的分布式锁执行器
     */
    private DistributedLockExecutorStrategy primaryExecutor;

    /**
     * 获取锁逻辑
     *
     * @param key            获取锁的key
     * @param acquireTimeout 获取锁获取超时时间
     * @param expireTime     锁过期时间
     *                       业务逻辑未执行完成的话redisson会自动续期
     * @return RLock 锁实例对象
     */
    public DistributedLockInfo lock(String key, long acquireTimeout, long expireTime,
                                    Class<? extends DistributedLockExecutorStrategy> distributedLockExecutorStrategy) {
        acquireTimeout = acquireTimeout <= 0 ? distributedLockProperties.getAcquireTimeout() : acquireTimeout;
        expireTime = expireTime <= 0 ? distributedLockProperties.getExpire() : expireTime;
        //获取对应的分布式锁执行器
        DistributedLockExecutorStrategy executorStrategy = obtainExecutor(distributedLockExecutorStrategy);
        //对应执行器获取分布式锁，默认redisson
        RLock rLock = executorStrategy.acquireLock(key, acquireTimeout, expireTime);
        if (rLock != null) {
            return new DistributedLockInfo(key, expireTime, acquireTimeout, rLock, executorStrategy);
        }
        return null;
    }

    /**
     * 释放锁逻辑
     *
     * @param distributedLockInfo 锁对象信息
     * @return true解锁成功 false解锁失败
     */
    public boolean releaseLock(DistributedLockInfo distributedLockInfo) {
        if (null == distributedLockInfo) {
            return false;
        }
        return distributedLockInfo.getExecutorStrategy().releaseLock(distributedLockInfo.getRLockInstance());
    }

    /**
     * 获取执行器
     *
     * @param distributedLockExecutorStrategy 入参执行器
     * @return
     */
    protected DistributedLockExecutorStrategy obtainExecutor(Class<? extends DistributedLockExecutorStrategy> distributedLockExecutorStrategy) {
        if (distributedLockExecutorStrategy == null || distributedLockExecutorStrategy == DistributedLockExecutorStrategy.class) {
            return primaryExecutor;
        }
        DistributedLockExecutorStrategy executorStrategy = executorMap.get(distributedLockExecutorStrategy);
        Assert.notNull(executorStrategy, "未获取到对应的分布式锁执行器");
        return executorStrategy;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        //初始化DistributedLockTemplate的值
        //参数校验
        Assert.isTrue(distributedLockProperties.getAcquireTimeout() >= 0, "分布式锁获取超时时间必须大于0");
        Assert.isTrue(distributedLockProperties.getExpire() >= 0, "分布式锁过期时间必须大于0");
        //组装executorMap信息
        for (DistributedLockExecutorStrategy executor : executors) {
            executorMap.put(executor.getClass(), executor);
        }
        //配置文件配置的默认执行器
        Class<? extends DistributedLockExecutorStrategy> primaryExecutorStrategy = distributedLockProperties.getPrimaryExecutorStrategy();
        if (primaryExecutorStrategy == null) {
            this.primaryExecutor = executorMap.get(DefaultDistributedLockExecutorStrategy.class);
        } else {
            this.primaryExecutor = executorMap.get(primaryExecutorStrategy);
            Assert.notNull(primaryExecutor, "未获取到对应的分布式锁执行器");
        }
    }
}
